Coverage Report

Created: 2024-12-26 12:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\api\core\generator.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::api::core::Error;
30
use crate::compiler;
31
use crate::compiler::util::imports::ImportSolver;
32
use crate::gen::codec::CodecMap;
33
use crate::gen::file::FileType;
34
use bp3d_debug::trace;
35
use bp3d_util::index_map::IndexMap;
36
use bp3d_util::path::PathExt;
37
use std::marker::PhantomData;
38
use std::path::{Path, PathBuf};
39
use crate::api::core::loader::Options;
40
use crate::compiler::util::protocols::ProtocolStore;
41
42
pub struct Context<'a, I: ImportSolver> {
43
    items: IndexMap<Item<'a>>,
44
    pub protocols: &'a ProtocolStore<'a, I, Options<'a>>,
45
}
46
47
impl<'a, I: ImportSolver> Context<'a, I> {
48
3
    pub fn new(protocols: &'a ProtocolStore<'a, I, Options<'a>>) -> Self {
49
3
        Self {
50
3
            items: IndexMap::with_capacity(protocols.len()),
51
3
            protocols,
52
3
        }
53
3
    }
54
55
12
    pub fn generate<G: crate::gen::Generator>(
56
12
        &mut self,
57
12
        generator: &Generator<G>,
58
12
        full_name: impl AsRef<str>,
59
12
        params: &Params,
60
12
        generator_params: &G::Params<'_>,
61
12
    ) -> Result<(), Error> {
62
12
        if self.get(full_name.as_ref()).is_none() {
  Branch (62:12): [True: 12, False: 0]
  Branch (62:12): [True: 0, False: 0]
  Branch (62:12): [Folded - Ignored]
63
12
            let entry = self
64
12
                .protocols
65
12
                .entry(full_name.as_ref())
66
12
                .ok_or_else(|| 
Error::ProtocolNotFound(full_name.as_ref().into())0
)
?0
;
67
12
            if !entry.userdata.is_excluded_from_generation() {
  Branch (67:16): [True: 12, False: 0]
  Branch (67:16): [True: 0, False: 0]
  Branch (67:16): [Folded - Ignored]
68
12
                self.items.insert(generator.generate(&entry.model, params, generator_params)
?0
);
69
0
            }
70
0
        }
71
12
        Ok(())
72
12
    }
73
74
3
    pub fn generate_all<G: crate::gen::Generator>(
75
3
        &mut self,
76
3
        generator: &Generator<G>,
77
3
        params: &Params,
78
3
        generator_params: &G::Params<'_>,
79
3
    ) -> Result<(), Error> {
80
38
        for entry in 
self.protocols.entries()3
{
81
38
            if self.get(&entry.model.full_name).is_none() && 
!entry.userdata.is_excluded_from_generation()26
{
  Branch (81:16): [True: 26, False: 12]
  Branch (81:62): [True: 25, False: 1]
  Branch (81:16): [True: 0, False: 0]
  Branch (81:62): [True: 0, False: 0]
  Branch (81:16): [Folded - Ignored]
  Branch (81:62): [Folded - Ignored]
82
25
                self.items.insert(generator.generate(&entry.model, params, generator_params)
?0
);
83
13
            }
84
        }
85
3
        Ok(())
86
3
    }
87
88
50
    pub fn get(&self, full_name: &str) -> Option<&Item<'a>> {
89
50
        self.items.get(full_name)
90
50
    }
91
92
3
    pub fn iter(&self) -> impl Iterator<Item = &Item<'a>> {
93
3
        self.items.iter()
94
3
    }
95
}
96
97
pub struct Item<'a> {
98
    full_name: &'a str,
99
    pub name: &'a str,
100
    pub path: PathBuf,
101
}
102
103
impl bp3d_util::index_map::Index for Item<'_> {
104
    type Key = str;
105
106
52
    fn index(&self) -> &Self::Key {
107
52
        self.full_name
108
52
    }
109
}
110
111
#[derive(Copy, Clone, Debug)]
112
pub struct Params {
113
    pub write_messages: bool,
114
    pub read_messages: bool,
115
    pub use_enums: bool,
116
    pub use_structs: bool,
117
    pub use_messages: bool,
118
    pub use_unions: bool,
119
}
120
121
impl Default for Params {
122
15
    fn default() -> Self {
123
15
        Self {
124
15
            write_messages: true,
125
15
            read_messages: true,
126
15
            use_enums: true,
127
15
            use_structs: true,
128
15
            use_messages: true,
129
15
            use_unions: true,
130
15
        }
131
15
    }
132
}
133
134
impl Params {
135
0
    pub fn new() -> Self {
136
0
        Self {
137
0
            write_messages: false,
138
0
            read_messages: false,
139
0
            use_enums: false,
140
0
            use_structs: false,
141
0
            use_messages: false,
142
0
            use_unions: false,
143
0
        }
144
0
    }
145
}
146
147
pub struct Generator<'a, G> {
148
    generator: PhantomData<G>,
149
    out_directory: &'a Path,
150
    file_header: Option<&'a Path>,
151
    codec_map: CodecMap<'a, 'a>,
152
}
153
154
impl<'a, G: crate::gen::Generator> Generator<'a, G> {
155
3
    pub fn new(out_directory: &'a Path, codecs: CodecMap<'a, 'a>, _: G) -> Self {
156
3
        Self {
157
3
            out_directory,
158
3
            generator: PhantomData,
159
3
            file_header: None,
160
3
            codec_map: codecs,
161
3
        }
162
3
    }
163
164
0
    pub fn with_default_codecs(out_directory: &'a Path, _: G) -> Self {
165
0
        Self {
166
0
            out_directory,
167
0
            generator: PhantomData,
168
0
            file_header: None,
169
0
            codec_map: G::get_default_codecs(),
170
0
        }
171
0
    }
172
173
0
    pub fn set_file_header(&mut self, path: &'a Path) -> &mut Self {
174
0
        self.file_header = Some(path);
175
0
        self
176
0
    }
177
178
37
    pub fn generate<'b>(
179
37
        &self,
180
37
        protocol: &'b compiler::Protocol,
181
37
        params: &Params,
182
37
        generator_params: &G::Params<'_>,
183
37
    ) -> Result<Item<'b>, Error> {
184
37
        trace!({?params}, "Generating protocol {}", protocol.full_name);
185
37
        let file_header = self
186
37
            .file_header
187
37
            .map(std::fs::read_to_string)
188
37
            .transpose()
189
37
            .map_err(Error::Io)
?0
190
37
            .map(|v| 
G::generate_file_header(v.lines())0
);
191
37
        let name = protocol.name();
192
37
        let files =
193
37
            G::generate(protocol, &self.codec_map, generator_params).map_err(|e| 
Error::Generator(e.to_string())0
)
?0
;
194
37
        let out_path = self.out_directory.join(name);
195
37
        if !out_path.exists() {
  Branch (195:12): [True: 37, False: 0]
  Branch (195:12): [True: 0, False: 0]
  Branch (195:12): [Folded - Ignored]
196
37
            std::fs::create_dir(&out_path).map_err(Error::Io)
?0
;
197
0
        }
198
223
        let 
files_iter = files.into_iter().filter(37
|v| {
199
223
            let value = match v.ty() {
200
37
                FileType::MessageWriting => params.write_messages,
201
38
                FileType::MessageReading => params.read_messages,
202
37
                FileType::Message => params.use_messages,
203
37
                FileType::Structure => params.use_structs,
204
37
                FileType::Enum => params.use_enums,
205
37
                FileType::Union => params.use_unions,
206
            };
207
223
            if !value {
  Branch (207:16): [True: 2, False: 221]
  Branch (207:16): [True: 0, False: 0]
  Branch (207:16): [Folded - Ignored]
208
2
                trace!("Excluding {:?} from generation", v);
209
221
            } else {
210
221
                trace!("Including {:?} in generation", v);
211
221
            }
212
223
            value
213
223
        }
)37
;
214
37
        let iter = files_iter
215
37
            .into_iter()
216
221
            .map(|v| v.write(&out_path, file_header.as_deref(), G::get_language_extension()))
217
221
            .filter_map(|v| match v {
218
221
                Ok(o) => o.map(Ok),
219
0
                Err(e) => Some(Err(e)),
220
221
            })
221
37
            .collect::<std::io::Result<Vec<PathBuf>>>()
222
37
            .map_err(Error::Io)
?0
;
223
96
        let 
umbrella37
=
G::generate_umbrella(name, iter.iter().map(37
|v| &**v), generator_params)
224
37
            .map_err(|e| 
Error::Generator(e.to_string())0
)
?0
;
225
37
        let proto_path = if umbrella.len() > 1 {
  Branch (225:29): [True: 37, False: 0]
  Branch (225:29): [True: 0, False: 0]
  Branch (225:29): [Folded - Ignored]
226
37
            let umbrella_path = out_path.join("umbrella").ensure_extension(G::get_language_extension()).to_path_buf();
227
37
            std::fs::write(&umbrella_path, umbrella).map_err(Error::Io)
?0
;
228
37
            umbrella_path
229
        } else {
230
0
            out_path
231
        };
232
37
        Ok(Item {
233
37
            full_name: &protocol.full_name,
234
37
            name,
235
37
            path: proto_path,
236
37
        })
237
37
    }
238
}